package class

import (
	"log"
	"strings"
	"unsafe"

	"gitee.com/microwww/jvm-go/jvm/classfile"
)

const ( // classfile file access
	// Flag Name     Value     Interpretation
	ACC_PUBLIC       = 0x0001 // Declared public; may be accessed from outside its package. 可用于 class, filed
	ACC_PRIVATE      = 0x0002 // Declared private; usable only within the defining class.   可用于 -----, filed
	ACC_PROTECTED    = 0x0004 // Declared protected; may be accessed within subclasses.     可用于 -----, filed
	ACC_STATIC       = 0x0008 // Declared static.                                           可用于 -----, filed
	ACC_FINAL        = 0x0010 // Declared final; no subclasses allowed.                     可用于 class, filed
	ACC_SUPER        = 0x0020 // Treat superclass methods specially when invoked by the invokespecial instruction.
	ACC_SYNCHRONIZED = 0x0020 // Declared synchronized; invocation is wrapped by a monitor use. 可用于 method
	ACC_BRIDGE       = 0x0040 // A bridge method, generated by the compiler.                    可用于 method
	ACC_VOLATILE     = 0x0040 // Declared volatile; cannot be cached.
	ACC_VARARGS      = 0x0080 // Declared with variable number of arguments.可用于 method
	ACC_TRANSIENT    = 0x0080 // Declared transient; not written or read by a persistent object manager.
	ACC_NATIVE       = 0x0100 //	Declared native; implemented in a language other than Java. 可用于 method
	ACC_INTERFACE    = 0x0200 // Is an interface, not a classfile.
	ACC_ABSTRACT     = 0x0400 // Declared abstract; must not be instantiated.
	ACC_STRICT       = 0x0800 // Declared strictfp; floating-point mode is FP-strict.
	ACC_SYNTHETIC    = 0x1000 // Declared synthetic; not present in the source code.            可以用于 class, method
	ACC_ANNOTATION   = 0x2000 // Declared as an annotation type.
	ACC_ENUM         = 0x4000 // Declared as an enum type.
	// FIELD : ACC_FINAL  =  0x0010    // Declared final; never directly assigned to after object construction (JLS §17.5).
)

// 初始化自己的函数
var init_static = make(map[int32]func(*Class), 8)

// todo :: 修改名字
func PutInitStatic(f func(*Class)) {
	pin := int32(uintptr(unsafe.Pointer(&f)))
	init_static[pin] = f
}

func (c *Class) initStaticVars() {
	for _, f := range init_static {
		f(c)
	}
	for _, f := range c.Fields() {
		if f.IsStatic() { // && !f.IsFinal()
			desc := c.ClassLoader().DefineClass(f.TypeName())
			_, ok := c.staticVars[f.name] // 没有被初始化的进行初始化
			if !ok {
				c.staticVars[f.Name()] = desc.DefValue()
				log.Default().Printf("Init class static field %v.%v = %v(%T)%v",
					f.Class().Name(), f.Name(), desc.DefValue(), desc.DefValue(), f.Descriptor())
			}
		}
	}
}

// public class Name extends xxx<???> impliment xxxx,xxxx {}

type Class struct {
	access         uint16
	name           string
	superClassName string
	superClass     *Class
	interfacesName []string
	interfaces     []*Class
	cp             *ConstantPool
	fields         []*Field
	methods        []*Method
	staticVars     map[string]interface{}
	defValue       interface{}
	jclass         *Object
}

var classListener = make(map[string]map[int32]func(*Class), 255)

func AddClassListener(clazz string, fun func(*Class)) {
	p := int32(uintptr(unsafe.Pointer(&fun)))
	v := classListener[clazz]
	if v == nil {
		classListener[clazz] = make(map[int32]func(*Class), 8)
	}
	classListener[clazz][p] = fun
}

func (c *Class) Jclass() *Object {
	if c.jclass == nil {
		cz := c.ClassLoader().DefineClass("java/lang/Class")
		c.jclass = NewObject(cz)
	}
	return c.jclass
}

func (c *Class) Name() string {
	return c.name
}

func (c *Class) NameJava() string {
	name := strings.ReplaceAll(c.name, "/", ".")
	i := strings.Index(name, "$") // 内部类
	if i >= 0 {
		name = strings.Replace(name, "$", ".", 1)
	}
	return name
}

func (c *Class) IsArray() bool {
	// return strings.HasSuffix(c.Name(), "[")
	return c.name[0] == '['
}

func (c *Class) IsAssignableFrom(clz *Class) bool {
	if c == clz {
		return true
	}
	if clz.IsInterface() {
		return c.isImplements(clz)
	} else {
		return c.isSubClassOf(clz)
	}
}

func (c *Class) isSubClassOf(parent *Class) bool {
	for ch := c; ch != nil; ch = ch.superClass {
		if ch == parent {
			return true
		}
	}
	return false
}

func (c *Class) isSubInterface(parent *Class) bool {
	for _, f := range c.interfaces {
		if f == parent || f.isSubInterface(parent) {
			return true
		}
	}
	return false
}

func (c *Class) isImplements(parent *Class) bool {
	for ch := c; ch != nil; ch = ch.superClass {
		for _, clzz := range ch.interfaces {
			if clzz == parent {
				return true
			}
			ok := clzz.isSubInterface(parent)
			if ok {
				return true
			}
		}
	}
	return false
}

func (c *Class) StaticVars(name string) interface{} {
	e, ok := c.staticVars[name]
	if !ok {
		panic("Not init static value: " + name)
	}
	return e
}

func (c *Class) SetStaticVars(name string, val interface{}) {
	c.staticVars[name] = val
}

func (c *Class) Fields() []*Field {
	return c.fields
}

func (c *Class) Methods() []*Method {
	return c.methods
}

func (c *Class) GetMethod(name, descriptor string) *Method {
	for _, method := range c.methods {
		if method.name == name && method.descriptor == descriptor {
			return method
		}
	}
	return nil
}

func (c *Class) ClassLoader() *ClassLoader {
	return c.cp.classLoader
}

func (c *Class) SuperClass() *Class {
	return c.superClass
}

func (c *Class) IsInitStaticVars() bool {
	return c.staticVars != nil
}

func (c *Class) InitStaticVars() bool {
	if c.staticVars == nil {
		c.staticVars = make(map[string]interface{}, 32)
		return true
	}
	return false
}

func (c *Class) ConstantPool() *ConstantPool {
	return c.cp
}

func (c *Class) IsPublic() bool {
	return c.access&uint16(ACC_PUBLIC) != 0
}

func (c *Class) IsInterface() bool {
	return c.access&uint16(ACC_INTERFACE) != 0
}

func (c *Class) IsAbstract() bool {
	return c.access&uint16(ACC_ABSTRACT) != 0
}

func (c *Class) IsAccessibleTo(other *Class) bool {
	return c.IsPublic() || other.GetPackageName() == c.GetPackageName()
}

func (c *Class) GetPackageName() string {
	if i := strings.LastIndex(c.name, "/"); i >= 0 {
		return c.name[:i]
	}
	return ""
}

func (c *Class) DefValue() interface{} {
	return c.defValue
}

// newClass : classLoader 仍然没有被初始化
func (c *ClassLoader) newClass(clazz *classfile.ClassFile) *Class {
	clz := &Class{}
	clz.name = clazz.ThisClass()
	clz.access = clazz.AccessFlags()
	clz.superClassName = clazz.SuperClass()
	info := clazz.InterfaceInfo()
	clz.interfacesName = make([]string, len(info))
	for i, f := range info {
		clz.interfacesName[i] = f.Name()
	}
	clz.cp = &ConstantPool{
		class: clz,
		// consts:      LoadConstantPool(c, clazz.ConstantPool()),
		classLoader: c,
	}
	clz.cp.loadConstantPool(clazz.ConstantPool())
	clz.fields = newFields(clz, clazz)
	clz.methods = newMethods(clz, clazz)
	return clz
}

func newFields(clz *Class, clazz *classfile.ClassFile) []*Field {
	infos := clazz.FieldInfo()
	iis := make([]*Field, len(infos))
	for i, m := range infos {
		iis[i] = &Field{
			Member{
				accessFlags: m.AccessFlags(),
				name:        m.Name(),
				descriptor:  m.Descriptor(),
				class:       clz,
			},
		}
	}
	return iis
}

func newMethods(clz *Class, clazz *classfile.ClassFile) []*Method {
	infos := clazz.MethodInfo()
	iis := make([]*Method, len(infos))

	for i, m := range infos {
		code := m.CodeAttribute()
		if code == nil { // native will nil
			code = &classfile.CodeAttribute{}
		}
		iis[i] = &Method{
			Member: Member{
				accessFlags: m.AccessFlags(),
				name:        m.Name(),
				descriptor:  m.Descriptor(),
				class:       clz,
			},
			max_stack:  code.MaxStack(),
			max_locals: code.MaxLocals(),
			code:       code.Code(),
		}
		iis[i].ParseMethodArgs()
		if iis[i].IsNative() {
			injectCodeAttribute(iis[i])
		}
	}
	return iis
}

func injectCodeAttribute(mth *Method) {
	returnType := mth.ret
	mth.max_stack = 4
	mth.max_locals = uint16(mth.args_count)
	switch returnType[0] {
	case 'V':
		mth.code = []byte{0xfe, 0xb1} // return
	case 'D':
		mth.code = []byte{0xfe, 0xaf} // dreturn
	case 'F':
		mth.code = []byte{0xfe, 0xae} // freturn
	case 'J':
		mth.code = []byte{0xfe, 0xad} // lreturn
	case 'L', '[':
		mth.code = []byte{0xfe, 0xb0} // areturn
	default:
		mth.code = []byte{0xfe, 0xac} // ireturn
	}
}

func (m *Method) ParseMethodArgs() {
	desc := m.descriptor
	if !strings.HasPrefix(desc, "(") || !strings.Contains(desc, ")") {
		panic("Must start with `(` : ({ParameterDescriptor}) ReturnDescriptor")
	}
	sp := strings.Split(desc, ")")
	m.args, m.args_count = ParseArgs(sp[0][1:])

	if !m.IsStatic() {
		m.args_count += 1
	}

	m.ret = sp[1]
}

func ParseArgs(str string) ([]string, int) {
	args := make([]string, 0, 8)
	count := 0
	for i := 0; i < len(str); i++ {
		v, index, c := parseOne(str, i)
		count += c
		args = append(args, v)
		i = index
	}
	return args, count
}

func parseOne(str string, i int) (string, int, int) {
	count := 0
	switch str[i] {
	case 'J', 'D': // long , double ++
		count++
		fallthrough
	case 'Z', 'B', 'C', 'S', 'I', 'F':
		count++
		return str[i : i+1], i, count
	case 'L':
		count++
		n := strings.Index(str[i:], ";")
		return str[i : i+n+1], i + n, count
	case '[':
		count++
		v, i, _ := parseOne(str, i+1)
		return "[" + v, i, count
	default:
		panic("Not support field descriptor: " + str[i:i+1])
	}
	return "", i, 0
}

// public String name;

type Member struct {
	accessFlags uint16
	name        string
	descriptor  string
	class       *Class
}

func (c *Member) Class() *Class {
	return c.class
}

func (c *Member) isProtected() bool {
	return c.accessFlags&uint16(ACC_PROTECTED) != 0
}

func (c *Member) IsPrivate() bool {
	return c.accessFlags&uint16(ACC_PRIVATE) != 0
}

func (c *Member) IsFinal() bool {
	return c.accessFlags&uint16(ACC_FINAL) != 0
}

func (c *Member) IsStatic() bool {
	return c.accessFlags&uint16(ACC_STATIC) != 0
}

func (c *Member) IsNative() bool {
	return c.accessFlags&uint16(ACC_NATIVE) != 0
}

func (c *Member) IsPublic() bool {
	return c.accessFlags&uint16(ACC_PUBLIC) != 0
}

func (c *Member) IsProtected() bool {
	return c.accessFlags&uint16(ACC_PROTECTED) != 0
}

func (c *Member) IsAbstract() bool {
	return c.accessFlags&uint16(ACC_ABSTRACT) != 0
}

func (c *Member) IsSuper() bool {
	return c.accessFlags&uint16(ACC_SUPER) != 0
}

func (m *Member) Modifier() string {
	v := ""
	if m.IsPublic() {
		v += "public "
	}
	if m.IsStatic() {
		v += "static "
	}
	if m.IsFinal() {
		v += "final "
	}
	if m.IsAbstract() {
		v += "abstract "
	}
	if m.IsNative() {
		v += "native "
	}
	return v
}

func (c *Member) GetPackageName() string {
	if i := strings.LastIndex(c.name, "/"); i >= 0 {
		return c.name[:i]
	}
	return ""
}

func TypeString(de string) string {
	l := len(de)
	if l > 2 && de[0] == 'L' { // Ljava.lang.String;
		de = de[1 : l-1]
	}
	return de
}

func (c *Member) isAccessibleTo(other *Class) bool {
	clz := c.class
	if c.IsPublic() || clz == other {
		return true
	}
	if c.isProtected() {
		return other.isSubClassOf(clz) || clz.GetPackageName() == other.GetPackageName()
	}
	if !c.IsPrivate() {
		return clz.GetPackageName() == other.GetPackageName()
	}
	return false
}

func (m *Member) Name() string {
	return m.name
}

func (m *Member) Descriptor() string {
	return m.descriptor
}

type Field struct {
	Member
}

func (m *Field) String() string {
	return m.Name()
}

func (m *Field) TypeName() string {
	return TypeString(m.Descriptor())
}

type Method struct {
	Member
	max_stack  uint16
	max_locals uint16
	args       []string
	args_count int
	ret        string
	code       []byte
}

func (m *Method) MaxStack() uint16 {
	return m.max_stack
}

func (m *Method) MaxLocals() uint16 {
	return m.max_locals
}

func (m *Method) Code() []byte {
	return m.code
}

func (m *Method) String() string {
	return m.Name() + "" + m.descriptor
}

func (m *Method) Return() string {
	return m.ret
}

func (m *Method) ReturnType() *Class {
	name := TypeString(m.ret)
	return m.class.ClassLoader().DefineClass(name)
}

func (m *Method) Args() []string {
	return m.args
}

func (m *Method) ArgsCount() int {
	return m.args_count
}
